package com.qianxx.base.uploadFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
import java.util.Set;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.util.ByteArrayBuffer;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;

import com.qianxx.base.utils.LogUtil;
import com.qianxx.base.utils.SPUtil;

public class HttpRequester {
	public static final int UPLOAD_IMAGE = 0;
	public static final int UPLOAD_PARAMS = 1;
	final String BOUNDARY = "---------------------------7da2137580612"; // 数据分隔线
	final String ENDLINE = "--" + BOUNDARY + "--\r\n"; // 数据结束标志
	private static HttpRequester request;
	private UploadFileAsyncTask uploadFileAsyncTask;
	private String uploadURL;
	private Map<String, String> params;
	// private Map < String , String[] > paramsArray;
	private FormFile[] files;
	private static int dataLength;
	private OnRequestListener onRequestListener;
	private static final int WHAT_SUCCESS = 10;// 成功
	private static final int WHAT_PROGRESS = 11;// 上传过程
	private static final int WHAT_NETWORK = 12;// 失败

	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case WHAT_SUCCESS:
				// TODO 上传完成
				onRequestListener.onFinish((String) msg.obj);
				break;
			case WHAT_PROGRESS:
				// TODO 上传过程
				onRequestListener.onProgressing(msg.arg1, dataLength);
				break;
			case WHAT_NETWORK:

				break;

			default:
				break;
			}
		}
	};

	private static Context mContext;

	public interface OnRequestListener {
		void onProgressing(int size, int allSize);

		void onFinish(String response);
	}

	public static HttpRequester getInstanse(Context mContext) {
		HttpRequester.mContext = mContext;
		if (request == null) {
			request = new HttpRequester();
		}
		return request;
	}

	private String token;
	
	public String getToken() {
		return token;
	}

	public void setToken(String token) {
		this.token = token;
	}

	public void startRequest(String uploadURL, Map<String, String> params,
			OnRequestListener listener) {
		this.params = params;
		this.uploadURL = uploadURL;
		this.onRequestListener = listener;
		request.uploadFileAsyncTask = new UploadFileAsyncTask();
		request.uploadFileAsyncTask.execute(UPLOAD_PARAMS);
	}

	public void startUpload(String uploadURL, Map<String, String> params,
			FormFile[] files, OnRequestListener listener) {
		this.params = params;
		this.files = files;
		this.uploadURL = uploadURL;
		this.onRequestListener = listener;
		request.uploadFileAsyncTask = new UploadFileAsyncTask();
		request.uploadFileAsyncTask.execute(UPLOAD_IMAGE);
	}

	class UploadFileAsyncTask extends AsyncTask<Integer, Integer, String> {
		@Override
		protected String doInBackground(Integer... params) {
			String resultStr = "";
			try {
				switch (params[0]) {
				case UPLOAD_IMAGE:
					resultStr = post(uploadURL, HttpRequester.this.params,
							files);
				case UPLOAD_PARAMS:// 这个尚有问题未解决，直接调用第一个也可以 fill传null也可以
					resultStr = post(uploadURL, HttpRequester.this.params);
				default:
					break;
				}
			} catch (IOException e) {
				// resultStr = getFailMsg(e);
				e.printStackTrace();
			}
			return resultStr;
		}

		@Override
		protected void onPostExecute(String result) {
			// Toast.makeText(mContext, result, Toast.LENGTH_SHORT).show();
			// TODO 上传完成
			onRequestListener.onFinish(result);
		}

		@Override
		protected void onProgressUpdate(Integer... values) {
			// TODO 上传过程
			// onRequestListener.onProgressing ( values[ 0 ], dataLength );
		}

		/**
		 * @描述 根据Exception构造返回信息，在Activity做具体处理
		 * @方法名 getFailMsg(Exception e)
		 * @param e
		 * @return String
		 */
		private String getFailMsg(Exception e) {
			int failCode;
			if (e instanceof ConnectTimeoutException) {
				failCode = ErrorConfig.ConnectTimeoutException;
			} else if (e instanceof ClientProtocolException) {
				failCode = ErrorConfig.ClientProtocolException;
			} else if (e instanceof IOException) {
				failCode = ErrorConfig.IOException;
			} else if (e instanceof MalformedURLException) {
				failCode = ErrorConfig.MalformedURLException;
			} else if (e instanceof ConnectionPoolTimeoutException) {
				failCode = ErrorConfig.ConnectionPoolTimeoutException;
			} else {
				failCode = ErrorConfig.UnknowException;
			}
			return "{\"errCode\":" + failCode + "}";
		}

		/**
		 * 只有参数上传
		 * 
		 * @param path
		 *            网络路径
		 * @param params
		 *            上传参数
		 * */
		private String post(String path, Map<String, String> params)
				throws IOException {
			if (params.size() > 0) {
				LogUtil.d("getRequestPOST --- 提交的参数如下：");
				Set<String> keySet = params.keySet();
				for (String key : keySet) {
					String value = params.get(key);
					LogUtil.d(key + " = " + value);
				}
			}
			LogUtil.d(path);
			String textEntity = createTextParams(params);
			dataLength = textEntity.toString().getBytes().length
					+ ENDLINE.getBytes().length;
			HttpURLConnection conn = request.getParamsConn(path);
			OutputStream outStream = conn.getOutputStream();
			// 把所有文本类型的实体数据发送出来
			outStream.write(textEntity.toString().getBytes());
			publishProgress(textEntity.toString().getBytes().length);
			// 下面发送数据结束标志，表示数据已经结束
			outStream.write(ENDLINE.getBytes());
			// publishProgress ( dataLength );
			handler.sendMessage(handler.obtainMessage(WHAT_PROGRESS,
					dataLength, 0));
			outStream.flush();
			outStream.close();
			String string = getResponse(conn);
			handler.sendMessage(handler.obtainMessage(WHAT_SUCCESS, string));
			LogUtil.d(string);
			return string;
		}

		/**
		 * 多文件上传 直接通过HTTP协议提交数据到服务器,实现如下面表单提交功能: <form method="POST"
		 * action="http://192.168.1.101:8083/upload/servlet/UploadServlet"
		 * enctype="multipart/form-data"> <input type="text" name="name"> <input
		 * type="text" name="id"> <input type="file" name="imagefile"> <input
		 * type="file" name="zip"> </form>
		 * 
		 * @param path
		 *            上传路径(注：避免使用localhost或127.0.0.1这样的路径测试，因为它会指向手机模拟器，
		 *            你可以使用http://www.iteye.cn或http://192.168.1.101:8083这样的路径测试)
		 * @param params
		 *            请求参数 key为参数名,value为参数值
		 * @param file
		 *            上传文件
		 * @throws IOException
		 */
		private String post(String path, Map<String, String> params,
				FormFile[] files) throws IOException {
			if (params.size() > 0) {
				LogUtil.d("getRequestPOST --- 提交的参数如下：");
				Set<String> keySet = params.keySet();
				for (String key : keySet) {
					String value = params.get(key);
					LogUtil.d(key + " = " + value);
				}
			}
			LogUtil.d(path);
			int hasUploadLength = 0;// 已上传内容的大小；
			int fileDataLength = computeFilesSize(files);// 计算上传文件的总大小
			String textEntity = createTextParams(params, files);// 将参数构造成String
																// 字符串
			int textLength = computeParamsSize(textEntity);// 计算上传参数的大小，包括：1、文件的参数。2、字符串的参数
			// 计算传输给服务器的实体数据总长度
			dataLength = textLength + fileDataLength
					+ ENDLINE.getBytes().length;
			HttpURLConnection conn = request.getFileConn(path);
			// if (conn.getResponseCode() != 200) {
			// return "{\"errCode\":" + 500 + "}";
			// }
			OutputStream outStream = conn.getOutputStream();
			// 把所有文本类型的实体数据发送出来
			outStream.write(textEntity.toString().getBytes());
			hasUploadLength += textLength;
			handler.sendMessage(handler.obtainMessage(WHAT_PROGRESS,
					hasUploadLength, 0));
			// publishProgress ( hasUploadLength );
			// 把所有文件类型的实体数据发送出来
			if (files != null) {
				for (FormFile uploadFile : files) {
					StringBuilder fileEntity = new StringBuilder();
					fileEntity.append("--");
					fileEntity.append(BOUNDARY);
					fileEntity.append("\r\n");
					fileEntity.append("Content-Disposition: form-data;name=\""
							+ uploadFile.getParameterName() + "\";filename=\""
							+ uploadFile.getFilname() + "\"\r\n");
					fileEntity.append("Content-Type: "
							+ uploadFile.getContentType() + "\r\n\r\n");
					outStream.write(fileEntity.toString().getBytes());
					hasUploadLength += fileEntity.toString().getBytes().length;
					// handler.sendMessage(handler.obtainMessage(WHAT_PROGRESS,
					// hasUploadLength, 0));
					// publishProgress ( hasUploadLength );
					if (uploadFile.getInStream() != null) {
						byte[] buffer = new byte[10240];
						int len = 0;
						while ((len = uploadFile.getInStream().read(buffer, 0,
								10240)) != -1) {
							outStream.write(buffer, 0, len);
							hasUploadLength += len;
							handler.sendMessage(handler.obtainMessage(
									WHAT_PROGRESS, hasUploadLength, 0));
							// publishProgress ( hasUploadLength );
						}
						uploadFile.getInStream().close();
					} else {
						outStream.write(uploadFile.getData(), 0,
								uploadFile.getData().length);
						hasUploadLength += uploadFile.getData().length;
						publishProgress(hasUploadLength);
					}
					outStream.write("\r\n".getBytes());
					hasUploadLength += "\r\n".getBytes().length;
				}
			}
			// 下面发送数据结束标志，表示数据已经结束
			outStream.write(ENDLINE.getBytes());
			hasUploadLength += ENDLINE.getBytes().length;
			handler.sendMessage(handler.obtainMessage(WHAT_PROGRESS,
					hasUploadLength, 0));
			// publishProgress ( hasUploadLength );
			outStream.flush();
			outStream.close();
			// 服务器返回数据
			String string = getResponse(conn);
			LogUtil.d(string);
			handler.sendMessage(handler.obtainMessage(WHAT_SUCCESS, string));
			return string;
		}

		private String getResponse(HttpURLConnection conn) throws IOException {
			InputStream is = conn.getInputStream();
			byte[] buffer = new byte[1024];
			ByteArrayBuffer byteBuffer = new ByteArrayBuffer(5000);
			int len = 0;
			while (-1 != (len = is.read(buffer))) {
				byteBuffer.append(buffer, 0, len);
			}
			String string = new String(byteBuffer.buffer(), 0,
					byteBuffer.length(), "utf-8");
			is.close();
			return string;
		}
	}

	/**
	 * 获取上传文件的网络连接
	 * 
	 * @param urlPath
	 *            连接地址
	 * @throws IOException
	 * */
	private HttpURLConnection getFileConn(String urlPath) throws IOException {
		URL url = new URL(urlPath);
		HttpURLConnection openConnection = (HttpURLConnection) url
				.openConnection();
		openConnection.setDoInput(true);
		openConnection.setDoOutput(true);
		openConnection.setUseCaches(false);

		String token = SPUtil.getInstance().getToken();
		if (!TextUtils.isEmpty(token)) {
			openConnection.setRequestProperty("token", token);
		}else {
			if (!TextUtils.isEmpty(getToken())) {
				openConnection.setRequestProperty("token", getToken());
			}
		}
		openConnection.setRequestMethod("POST");
		openConnection.setRequestProperty("Connection", "Keep-Alive");
		openConnection.setRequestProperty("Charset", "UTF-8");
		openConnection.setRequestProperty("Content-Length",
				String.valueOf(dataLength));
		openConnection.setRequestProperty("Content-Type",
				"multipart/form-data;boundary=" + BOUNDARY);
		return openConnection;
	}

	/**
	 * 获取参数请求的网络连接
	 * 
	 * @param urlPath
	 *            连接地址
	 * @throws IOException
	 * */
	private HttpURLConnection getParamsConn(String urlPath) throws IOException {
		URL url = new URL(urlPath);
		HttpURLConnection openConnection = (HttpURLConnection) url
				.openConnection();
		return openConnection;
	}

	/**
	 * 构造文本类型参数的实体数据
	 * 
	 * @param params
	 *            参数
	 * */
	private String createTextParams(Map<String, String> params) {
		return createTextParams(params, null);
	}

	/**
	 * 构造文本类型参数的实体数据
	 * 
	 * @param params
	 *            参数
	 * @param files
	 *            FormFile对象 ， 这里主要使用对象中的 fileDesc属性，（文件描述）
	 * */
	private String createTextParams(Map<String, String> params, FormFile[] files) {
		StringBuilder textEntity = new StringBuilder();
		for (Map.Entry<String, String> entry : params.entrySet()) {
			textEntity.append("--");
			textEntity.append(BOUNDARY);
			textEntity.append("\r\n");
			textEntity.append("Content-Disposition: form-data; name=\""
					+ entry.getKey() + "\"\r\n\r\n");
			textEntity.append(entry.getValue());
			textEntity.append("\r\n");
		}
		if (files != null && files.length > 0) {
			for (FormFile file : files) {
				if (file.getFileDesc() != null) {
					if (file.getFileDesc().isNeed()) {
						textEntity.append("--");
						textEntity.append(BOUNDARY);
						textEntity.append("\r\n");
						textEntity
								.append("Content-Disposition: form-data; name=\""
										+ "imageDesc" + "\"\r\n\r\n");
						textEntity.append(file.getFileDesc());
						textEntity.append("\r\n");
					}
				}
			}
		}
		return textEntity.toString();
	}

	/**
	 * 计算上传参数的总大小
	 * */
	private int computeUploadSize(FormFile[] files, String textEntity) {
		int length = 0;
		if (files != null) {
			length += computeFilesSize(files);
		}
		if (textEntity != null) {
			length += computeParamsSize(textEntity);
		}
		return length;
	}

	private int computeParamsSize(String textEntity) {
		return textEntity.getBytes().length;
	}

	/**
	 * 计算上传文件的大小
	 * */
	private int computeFilesSize(FormFile[] files) {
		int fileDataLength = 0;
		if (files != null) {
			for (FormFile uploadFile : files) {// 得到文件类型数据的总长度
				StringBuilder fileExplain = new StringBuilder();
				fileExplain.append("--");
				fileExplain.append(BOUNDARY);
				fileExplain.append("\r\n");
				fileExplain.append("Content-Disposition: form-data;name=\""
						+ uploadFile.getParameterName() + "\";filename=\""
						+ uploadFile.getFilname() + "\"\r\n");
				fileExplain.append("Content-Type: "
						+ uploadFile.getContentType() + "\r\n\r\n");
				fileExplain.append("\r\n");
				fileDataLength += fileExplain.length();
				if (uploadFile.getInStream() != null) {
					fileDataLength += uploadFile.getFile().length();
				} else {
					fileDataLength += uploadFile.getData().length;
				}
			}
		}
		return fileDataLength;
	}

	public void setOnRequestListener(OnRequestListener onRequestListener) {
		this.onRequestListener = onRequestListener;
	}

	public boolean stopRequest(boolean mayInterruptIfRunning) {
		if (request == null) {
			return false;
		}

		if (request.uploadFileAsyncTask == null) {
			return false;
		}

		if (!request.uploadFileAsyncTask.isCancelled()) {
			return request.uploadFileAsyncTask.cancel(mayInterruptIfRunning);
		} else {
			return false;
		}

	}

}
